#include "FeatureShape.h"
#include <BRepPrimAPI_MakeBox.hxx>
#include <BRepPrimAPI_MakeTorus.hxx>
#include <BRepPrimAPI_MakeSphere.hxx>
#include <gp_Parab.hxx>
#include <Geom_Parabola.hxx>
#include <BRepBuilderAPI_MakeEdge.hxx>
#include <gp_Hypr.hxx>
#include <Geom_Hyperbola.hxx>
#include <gp_Elips.hxx>
#include <Geom_Ellipse.hxx>
#include <BRepPrimAPI_MakeCylinder.hxx>
#include <BRepPrimAPI_MakeCone.hxx>
#include <GC_MakeArcOfCircle.hxx>
#include "Serialize/SerializeHelper.h"
#include "Core/RWTools.h"
#include <BRepBuilderAPI_MakePolygon.hxx>
#include <GC_MakeCircle.hxx>

FeatureShapeArcCircle::FeatureShapeArcCircle(const TopoDS_Shape& shape, const gp_Pnt& start, const gp_Pnt& tangent, const gp_Pnt& end)
    : FeatureShape(shape)
    , _start(start)
    , _tangent(tangent)
    , _end(end)
{
    _featureType = EFeatureType::ArcCircle;
}

FeatureShapeCone::FeatureShapeCone(const TopoDS_Shape& shape, float radius1, float radius2, float height)
    : FeatureShape(shape)
    , _radius1(radius1)
    , _radius2(radius2)
    , _height(height)
{
    _featureType = EFeatureType::Cone;
}

FeatureShapeCylinder::FeatureShapeCylinder(const TopoDS_Shape& shape, float radius, float height)
    : FeatureShape(shape)
    , _radius(radius)
    , _height(height)
{
    _featureType = EFeatureType::Cylinder;
}

FeatureShapeEllipse::FeatureShapeEllipse(const TopoDS_Shape& shape, const gp_Ax2& axis, float majorRadius, float minorRadius)
    : FeatureShape(shape)
    , _axis(axis)
    , _majorRadius(majorRadius)
    , _minorRadius(minorRadius)
{
    _featureType = EFeatureType::Ellipse;
}

FeatureShapeHyperbola::FeatureShapeHyperbola(const TopoDS_Shape& shape, const gp_Ax2& axis, float majorRadius, float minorRadius, float startLen, float endLen)
    : FeatureShape(shape)
    , _axis(axis)
    , _majorRadius(majorRadius)
    , _minorRadius(minorRadius)
    , _startLen(startLen)
    , _endLen(endLen)
{
    _featureType = EFeatureType::Hyperbola;
}

FeatureShapeLine::FeatureShapeLine(const TopoDS_Shape& shape, const gp_Pnt& start, const gp_Pnt& end)
    : FeatureShape(shape)
    , _start(start)
    , _end(end)
{
    _featureType = EFeatureType::Line;
}

FeatureShapeParabola::FeatureShapeParabola(const TopoDS_Shape& shape, const gp_Ax2& axis, float focal, float startLen, float endLen)
    : FeatureShape(shape)
    , _axis(axis)
    , _focal(focal)
    , _startLen(startLen)
    , _endLen(endLen)
{
    _featureType = EFeatureType::Parabola;
}

FeatureShapeTorus::FeatureShapeTorus(const TopoDS_Shape& shape, float radiusOuter, float radiusInner)
    : FeatureShape(shape)
    , _radiusOuter(radiusOuter)
    , _radiusInner(radiusInner)
{
    _featureType = EFeatureType::Torus;
}

FeatureShapeCube::FeatureShapeCube(const TopoDS_Shape& shape, float length, float width, float height)
    : FeatureShape(shape)
    , _length(length)
    , _width(width)
    , _height(height)
{
    _featureType = EFeatureType::Cube;
}

void FeatureShapeMix::deserialize(std::ifstream& in)
{
    in >> _position;

	// 读取 shapeFileName 的长度
	size_t nameLength;
	in.read(reinterpret_cast<char*>(&nameLength), sizeof(size_t));

	// 读取 shapeFileName 的内容
	if (nameLength > 0) {
		_shapeFileName.resize(nameLength);
		in.read(&_shapeFileName[0], nameLength);
	}
}

void FeatureShapeMix::serialize(std::ofstream& out) const
{
    out.write(reinterpret_cast<const char*>(&_featureType), sizeof(EFeatureType));

    out << _position;

	// 写入 shapeFileName 的长度
	size_t nameLength = _shapeFileName.size();
	out.write(reinterpret_cast<const char*>(&nameLength), sizeof(size_t));

	// 写入 shapeFileName 的内容
	if (nameLength > 0) {
		out.write(_shapeFileName.data(), nameLength);
	}
}

bool FeatureShapeMix::IsEqual(const FeatureShape& other) const
{
    return this->FeatureShape::IsEqual(other);
}

void FeatureShapeArcCircle::deserialize(std::ifstream& in)
{
    in >> _position >> _start >> _end >> _tangent;

	Handle(Geom_TrimmedCurve) arc = GC_MakeArcOfCircle(_start, _tangent, _end);
	TopoDS_Edge arcEdge = BRepBuilderAPI_MakeEdge(arc);
    _shape = arcEdge;
}

void FeatureShapeArcCircle::serialize(std::ofstream& out) const
{
    out.write(reinterpret_cast<const char*>(&_featureType), sizeof(EFeatureType));

    out << _position << _start << _end << _tangent;
}

bool FeatureShapeArcCircle::IsEqual(const FeatureShape& other) const
{
    if (!FeatureShape::IsEqual(other))
    {
        return false;
    }

    const FeatureShapeArcCircle& otherCircle = dynamic_cast<const FeatureShapeArcCircle&>(other);

    if (_start != otherCircle._start || _end != otherCircle._end || _tangent != otherCircle._tangent)
    {
        return false;
    }

    return true;
}

void FeatureShapeCone::deserialize(std::ifstream& in)
{
    in >> _position;
	in.read(reinterpret_cast<char*>(&_radius1), sizeof(float));
	in.read(reinterpret_cast<char*>(&_radius2), sizeof(float));
	in.read(reinterpret_cast<char*>(&_height), sizeof(float));

    BRepPrimAPI_MakeCone cone(_radius1, _radius2, _height);
    _shape = cone.Shape();
}

void FeatureShapeCone::serialize(std::ofstream& out) const
{
    out.write(reinterpret_cast<const char*>(&_featureType), sizeof(EFeatureType));

    out << _position;
    out.write(reinterpret_cast<const char*>(&_radius1), sizeof(float));
    out.write(reinterpret_cast<const char*>(&_radius2), sizeof(float));
    out.write(reinterpret_cast<const char*>(&_height), sizeof(float));
}

bool FeatureShapeCone::IsEqual(const FeatureShape& other) const
{
	if (!FeatureShape::IsEqual(other))
	{
		return false;
	}

    const FeatureShapeCone& otherCone = dynamic_cast<const FeatureShapeCone&>(other);

    return IsNearlyEqual(_radius1, otherCone._radius1)
        && IsNearlyEqual(_radius2, otherCone._radius2)
        && IsNearlyEqual(_height, otherCone._height);
}

void FeatureShapeCylinder::deserialize(std::ifstream& in)
{
    in >> _position;
	in.read(reinterpret_cast<char*>(&_radius), sizeof(float));
	in.read(reinterpret_cast<char*>(&_height), sizeof(float));

    BRepPrimAPI_MakeCylinder cylinder(_radius, _height);
    _shape = cylinder.Shape();
}

void FeatureShapeCylinder::serialize(std::ofstream& out) const
{
    out.write(reinterpret_cast<const char*>(&_featureType), sizeof(EFeatureType));

    out << _position;
    out.write(reinterpret_cast<const char*>(&_radius), sizeof(float));
    out.write(reinterpret_cast<const char*>(&_height), sizeof(float));
}

bool FeatureShapeCylinder::IsEqual(const FeatureShape& other) const
{
	if (!FeatureShape::IsEqual(other))
	{
		return false;
	}

    const FeatureShapeCylinder& otherCylinder = dynamic_cast<const FeatureShapeCylinder&>(other);

    return IsNearlyEqual(_height, otherCylinder._height)
        && IsNearlyEqual(_radius, otherCylinder._radius);
}

void FeatureShapeEllipse::deserialize(std::ifstream& in)
{
    in >> _position;
    in.read(reinterpret_cast<char*>(&_majorRadius), sizeof(float));
    in.read(reinterpret_cast<char*>(&_minorRadius), sizeof(float));
    in >> _axis;

	gp_Elips ellipse(_axis, _majorRadius, _minorRadius);
	Handle(Geom_Ellipse) geomEllipse = new Geom_Ellipse(ellipse);
	TopoDS_Edge ellipseEdge = BRepBuilderAPI_MakeEdge(geomEllipse);
	_shape = ellipseEdge;
}

void FeatureShapeEllipse::serialize(std::ofstream& out) const
{
    out.write(reinterpret_cast<const char*>(&_featureType), sizeof(EFeatureType));

    out << _position;
    out.write(reinterpret_cast<const char*>(&_majorRadius), sizeof(float));
    out.write(reinterpret_cast<const char*>(&_minorRadius), sizeof(float));
    out << _axis;
}

bool FeatureShapeEllipse::IsEqual(const FeatureShape& other) const
{
	if (!FeatureShape::IsEqual(other))
	{
		return false;
	}

    const FeatureShapeEllipse& otherEllipse = dynamic_cast<const FeatureShapeEllipse&>(other);

    if (_majorRadius != otherEllipse._majorRadius || _minorRadius != otherEllipse._minorRadius || _axis != otherEllipse._axis)
    {
        return false;
    }

    return true;
}

void FeatureShapeHyperbola::deserialize(std::ifstream& in)
{
    in >> _position;
    in.read(reinterpret_cast<char*>(&_majorRadius), sizeof(float));
    in.read(reinterpret_cast<char*>(&_minorRadius), sizeof(float));
    in >> _axis;
    in.read(reinterpret_cast<char*>(&_startLen), sizeof(float));
    in.read(reinterpret_cast<char*>(&_endLen), sizeof(float));

	gp_Hypr hyperbola(_axis, _majorRadius, _minorRadius);
	Handle(Geom_Hyperbola) geomHyperbola = new Geom_Hyperbola(hyperbola);
	TopoDS_Edge hyperbolaEdge = BRepBuilderAPI_MakeEdge(geomHyperbola, -_startLen, _endLen);
    _shape = hyperbolaEdge;
}

void FeatureShapeHyperbola::serialize(std::ofstream& out) const
{
    out.write(reinterpret_cast<const char*>(&_featureType), sizeof(EFeatureType));

    out << _position;
    out.write(reinterpret_cast<const char*>(&_majorRadius), sizeof(float));
    out.write(reinterpret_cast<const char*>(&_minorRadius), sizeof(float));
    out << _axis;
    out.write(reinterpret_cast<const char*>(&_startLen), sizeof(float));
    out.write(reinterpret_cast<const char*>(&_endLen), sizeof(float));
}

bool FeatureShapeHyperbola::IsEqual(const FeatureShape& other) const
{
	if (!FeatureShape::IsEqual(other))
	{
		return false;
	}

	const FeatureShapeHyperbola& otherHyperbola = dynamic_cast<const FeatureShapeHyperbola&>(other);

    if (!IsNearlyEqual(_majorRadius, otherHyperbola._majorRadius) ||
        !IsNearlyEqual(_minorRadius, otherHyperbola._minorRadius) ||
        !IsNearlyEqual(_startLen, otherHyperbola._startLen) ||
        !IsNearlyEqual(_endLen, otherHyperbola._endLen) ||
        _axis != otherHyperbola._axis)
    {
        return false;
    }

    return true;
}

void FeatureShapeLine::deserialize(std::ifstream& in)
{
    in >> _position >> _start >> _end;

    BRepBuilderAPI_MakeEdge edge(_start, _end);
    _shape = edge.Shape();
}

void FeatureShapeLine::serialize(std::ofstream& out) const
{
    out.write(reinterpret_cast<const char*>(&_featureType), sizeof(EFeatureType));

    out << _position << _start << _end;
}

bool FeatureShapeLine::IsEqual(const FeatureShape& other) const
{
	if (!FeatureShape::IsEqual(other))
	{
		return false;
	}

    const FeatureShapeLine& otherLine = dynamic_cast<const FeatureShapeLine&>(other);
    if (_start == otherLine._start && _end == otherLine._end)
    {
        return true;
    }

    return false;
}

void FeatureShapeParabola::deserialize(std::ifstream& in)
{
    in >> _position >> _axis;
    in.read(reinterpret_cast<char*>(&_focal), sizeof(float));
    in.read(reinterpret_cast<char*>(&_startLen), sizeof(float));
    in.read(reinterpret_cast<char*>(&_endLen), sizeof(float));

	gp_Parab parabola(_axis, _focal);
	Handle(Geom_Parabola) geomParabola = new Geom_Parabola(parabola);
	TopoDS_Edge parabolaEdge = BRepBuilderAPI_MakeEdge(geomParabola, -_startLen, _endLen);
    _shape = parabolaEdge;
}

void FeatureShapeParabola::serialize(std::ofstream& out) const
{
    out.write(reinterpret_cast<const char*>(&_featureType), sizeof(EFeatureType));

    out << _position << _axis;
    out.write(reinterpret_cast<const char*>(&_focal), sizeof(float));
    out.write(reinterpret_cast<const char*>(&_startLen), sizeof(float));
    out.write(reinterpret_cast<const char*>(&_endLen), sizeof(float));
}

bool FeatureShapeParabola::IsEqual(const FeatureShape& other) const
{
	if (!FeatureShape::IsEqual(other))
	{
		return false;
	}

    const FeatureShapeParabola& otherParabola = dynamic_cast<const FeatureShapeParabola&>(other);
    if (IsNearlyEqual(_focal, otherParabola._focal) &&
        IsNearlyEqual(_startLen, otherParabola._startLen) &&
        IsNearlyEqual(_endLen, otherParabola._endLen) &&
        otherParabola._axis == _axis)
    {
        return true;
    }

    return false;
}

void FeatureShapeSphere::deserialize(std::ifstream& in)
{
    in >> _position;
    in.read(reinterpret_cast<char*>(&_radius), sizeof(float));

    BRepPrimAPI_MakeSphere sphereShape(_position, _radius);
    _shape = sphereShape.Shape();
}

void FeatureShapeSphere::serialize(std::ofstream& out) const
{
    out.write(reinterpret_cast<const char*>(&_featureType), sizeof(EFeatureType));

    out << _position;
    out.write(reinterpret_cast<const char*>(&_radius), sizeof(float));
}

bool FeatureShapeSphere::IsEqual(const FeatureShape& other) const
{
	if (!FeatureShape::IsEqual(other))
	{
		return false;
	}

    const FeatureShapeSphere& otherSphere = dynamic_cast<const FeatureShapeSphere&>(other);
    if (IsNearlyEqual(_radius, otherSphere._radius))
    {
        return true;
    }
    return false;
}

void FeatureShapeTorus::deserialize(std::ifstream& in)
{
    in >> _position;
    in.read(reinterpret_cast<char*>(&_radiusOuter), sizeof(float));
    in.read(reinterpret_cast<char*>(&_radiusInner), sizeof(float));

    BRepPrimAPI_MakeTorus torusShape(_radiusOuter, _radiusInner);
    _shape = torusShape.Shape();
}

void FeatureShapeTorus::serialize(std::ofstream& out) const
{
    out.write(reinterpret_cast<const char*>(&_featureType), sizeof(EFeatureType));
    
    out << _position;
    out.write(reinterpret_cast<const char*>(&_radiusOuter), sizeof(float));
    out.write(reinterpret_cast<const char*>(&_radiusInner), sizeof(float));
}

bool FeatureShapeTorus::IsEqual(const FeatureShape& other) const
{
	if (!FeatureShape::IsEqual(other))
	{
		return false;
	}

    const FeatureShapeTorus& otherTorus = dynamic_cast<const FeatureShapeTorus&>(other);
	if (IsNearlyEqual(_radiusInner, otherTorus._radiusInner) &&
		IsNearlyEqual(_radiusOuter, otherTorus._radiusOuter))
	{
		return true;
	}
    return false;
}

void FeatureShapeCube::deserialize(std::ifstream& in)
{
    in >> _position;
    in.read(reinterpret_cast<char*>(&_length), sizeof(float));
    in.read(reinterpret_cast<char*>(&_width), sizeof(float));
    in.read(reinterpret_cast<char*>(&_height), sizeof(float));

	BRepPrimAPI_MakeBox cubeShape(_width, _length, _height);
	_shape = cubeShape.Shape();
}

void FeatureShapeCube::serialize(std::ofstream& out) const
{
    out.write(reinterpret_cast<const char*>(&_featureType), sizeof(EFeatureType));

    out << _position;
    out.write(reinterpret_cast<const char*>(&_length), sizeof(float));
    out.write(reinterpret_cast<const char*>(&_width), sizeof(float));
    out.write(reinterpret_cast<const char*>(&_height), sizeof(float));
}

bool FeatureShapeCube::IsEqual(const FeatureShape& other) const
{
	if (!FeatureShape::IsEqual(other))
	{
		return false;
	}

    const FeatureShapeCube& otherCube = dynamic_cast<const FeatureShapeCube&>(other);
	if (IsNearlyEqual(_length, otherCube._length) &&
		IsNearlyEqual(_height, otherCube._height) &&
        IsNearlyEqual(_width, otherCube._width))
	{
		return true;
	}
	return false;
}

std::shared_ptr<FeatureShape> FeatureShapeFactory::CreateFeatureShape(EFeatureType featureType)
{
    switch (featureType)
    {
    case EFeatureType::ArcCircle:
        return std::make_shared<FeatureShapeArcCircle>();
    case EFeatureType::Cone:
        return std::make_shared<FeatureShapeCone>();
    case EFeatureType::Cube:
        return std::make_shared<FeatureShapeCube>();
    case EFeatureType::Cylinder:
        return std::make_shared<FeatureShapeCylinder>();
    case EFeatureType::Ellipse:
        return std::make_shared<FeatureShapeEllipse>();
    case EFeatureType::Hyperbola:
        return std::make_shared<FeatureShapeHyperbola>();
    case EFeatureType::Line:
        return std::make_shared<FeatureShapeLine>();
    case EFeatureType::Mix:
        return std::make_shared<FeatureShapeMix>();
	case EFeatureType::Parabola:
		return std::make_shared<FeatureShapeParabola>();
	case EFeatureType::Sphere:
		return std::make_shared<FeatureShapeSphere>();
	case EFeatureType::Torus:
		return std::make_shared<FeatureShapeTorus>();
    case EFeatureType::Circle:
        return std::make_shared<FeatureShapeCircle>();
    case EFeatureType::Rectangle:
        return std::make_shared<FeatureShapeRectangle>();
    }

    std::cout << "FeatureShapeFactory::CreateFeatureShape: Unknown feature type" << std::endl;

    return nullptr;
}

bool FeatureShape::IsEqual(const FeatureShape& other) const
{
	if (_featureType != other._featureType || _position != other._position || _shape != other._shape)
	{
		return false;
	}

    return true;
}

FeatureShapeRectangle::FeatureShapeRectangle(const TopoDS_Shape& shape, const gp_Pnt& p1,
    const gp_Pnt& p2, const gp_Pnt& p3, const gp_Pnt& p4)
    : FeatureShape(shape)
    , _p1(p1)
    , _p2(p2)
    , _p3(p3)
    , _p4(p4)
{
    _featureType = EFeatureType::Rectangle;
}

void FeatureShapeRectangle::deserialize(std::ifstream& in)
{
    in >> _position >> _p1 >> _p2 >> _p3 >> _p4;

    BRepBuilderAPI_MakePolygon polygon(_p1, _p2, _p3, _p4, true);
    _shape = polygon.Wire();
}

void FeatureShapeRectangle::serialize(std::ofstream& out) const
{
    out.write(reinterpret_cast<const char*>(&_featureType), sizeof(EFeatureType));

    out << _position << _p1 << _p2 << _p3 << _p4;
}

bool FeatureShapeRectangle::IsEqual(const FeatureShape& other) const
{
	if (!FeatureShape::IsEqual(other))
	{
		return false;
	}

	const FeatureShapeRectangle& otherRect = dynamic_cast<const FeatureShapeRectangle&>(other);
	if (_p1 == otherRect._p1 && _p2 == otherRect._p2 &&
        _p3 == otherRect._p3 && _p4 == otherRect._p4)
	{
		return true;
	}

	return false;
}

FeatureShapeCircle::FeatureShapeCircle(const TopoDS_Shape& shape, const gp_Pnt& center, float radius, const gp_Dir& normal)
    : FeatureShape(shape)
    , _center(center)
    , _radius(radius)
    , _normal(normal)
{
    _featureType = EFeatureType::Circle;
}

void FeatureShapeCircle::deserialize(std::ifstream& in)
{
    in >> _position >> _center >> _normal;
    in.read(reinterpret_cast<char*>(&_radius), sizeof(float));

	Handle(Geom_Circle) circle = GC_MakeCircle(_center, _normal, _radius);
	TopoDS_Edge circleEdge = BRepBuilderAPI_MakeEdge(circle);
    _shape = circleEdge;
}

void FeatureShapeCircle::serialize(std::ofstream& out) const
{
    out.write(reinterpret_cast<const char*>(&_featureType), sizeof(EFeatureType));

    out << _position << _center << _normal;
    out.write(reinterpret_cast<const char*>(&_radius), sizeof(float));
}

bool FeatureShapeCircle::IsEqual(const FeatureShape& other) const
{
	if (!FeatureShape::IsEqual(other))
	{
		return false;
	}

	const FeatureShapeCircle& otherCircle = dynamic_cast<const FeatureShapeCircle&>(other);
	if (_center == otherCircle._center && _normal == otherCircle._normal && IsNearlyEqual(_radius, otherCircle._radius))
	{
		return true;
	}

	return false;
}
